package com.nbjtjc.safe.activiti.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.nbjtjc.safe.activiti.mapper.ExtActNodeMapper;
import com.nbjtjc.safe.activiti.model.DeployProcessDefine;
import com.nbjtjc.safe.activiti.model.ExtActNode;
import com.nbjtjc.safe.activiti.model.MyTask;
import com.nbjtjc.safe.bean.MyCache;
import com.nbjtjc.safe.bean.Result;
import com.nbjtjc.safe.util.ActUtils;
import com.nbjtjc.safe.util.FileUtil;
import com.nbjtjc.safe.util.MyShiroUtils;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @auther guqianbin
 * @date 2018/5/23 14:14
 * 流程相关的操作类
 **/
@Service
public class ActivitiService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private TaskService taskService;
    @Autowired
    IdentityService identityservice;
    @Autowired
    RuntimeService runtimeservice;
    @Autowired
    private FileUtil fileUtil;
    @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private ExtActNodeMapper extActNodeMapper;
    @Autowired
    HistoryService historyService;
    @Autowired
    private MyCache myCache;
    @Autowired
    private ActUtils actUtils;

    @PostConstruct
    public static void init() {
        ProcessEngineConfiguration processEngineConfiguration = Context.getProcessEngineConfiguration();
    }

    public Result getDeploymentBpmnFile(HttpServletResponse response){
        Result result = new Result();
         repositoryService.getResourceAsStream("542805","DangerProcess.bpmn20.xml");
         List<String> resources= repositoryService.getDeploymentResourceNames("542805");
         resources.forEach(name->{
             System.out.println(name);
         });
         result.setData(resources);
        return result;
    }
    public Result deploymentProcess(MultipartFile multipartFile, String name) throws IOException {
        Result result = new Result();
        long count = repositoryService.createProcessDefinitionQuery().count();
        System.out.println("部署前流程数量：" + count);
        String filePath = fileUtil.getFilePath();
        File targetFile = new java.io.File(filePath + multipartFile.getOriginalFilename());
        if (!targetFile.getParentFile().exists()) {
            targetFile.getParentFile().mkdirs();
        }
        multipartFile.transferTo(targetFile);
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        Deployment deployment = deploymentBuilder
                .name("webDeployment-" + name)
                .addInputStream(targetFile.getName() + "20.xml", new FileInputStream(targetFile))
                .deploy();
        //查询流程定义实体
        count = repositoryService.createProcessDefinitionQuery().count();
        System.out.println("部署后流程数量：" + count);
        if (ObjectUtil.isNull(deployment)) {
            result.setFalse("部署失败");
            return result;
        }
        createProcessExtActNode(deployment.getId());
        return result;
    }

    public Result getDeploymentProcess() {
        Result result = new Result();
        List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .list();
        List<DeployProcessDefine> deployProcessDefines = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(processDefinitions)) {
            processDefinitions.forEach(processDefinition -> {
                System.out.println("部署的id：" + processDefinition.getDeploymentId() + " 主键id：" +
                        processDefinition.getId() + " 名字：" + processDefinition.getName());
                DeployProcessDefine deployProcessDefine = new DeployProcessDefine();
                deployProcessDefine.setId(processDefinition.getId());
                deployProcessDefine.setKey(processDefinition.getKey());
                deployProcessDefine.setDeploymentId(processDefinition.getDeploymentId());
                deployProcessDefine.setVersion(processDefinition.getVersion());
                deployProcessDefine.setResourceName(processDefinition.getResourceName());
                deployProcessDefine.setName(processDefinition.getName());
                deployProcessDefines.add(deployProcessDefine);
            });
        }
        result.setData(deployProcessDefines);
        return result;
    }

    public List<MyTask> getHistoryTask(String processId) {
        List<HistoricTaskInstance> historicTaskInstanceList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processId).list();

        List<MyTask> myTaskList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(historicTaskInstanceList)) {
            historicTaskInstanceList.forEach(historicTaskInstance -> {
                MyTask myTask = new MyTask();
                myTask.setName(historicTaskInstance.getName());
                myTask.setProcessInstanceId(historicTaskInstance.getProcessInstanceId());
                myTask.setCreateTime(historicTaskInstance.getCreateTime());
                myTask.setEndTime(historicTaskInstance.getEndTime());
                myTask.setId(historicTaskInstance.getId());
                myTask.setUserId(historicTaskInstance.getAssignee());
                List<Comment> comments = taskService.getTaskComments(historicTaskInstance.getId());
                if (CollectionUtil.isNotEmpty(comments)) {
                    myTask.setComment(comments.get(0).getFullMessage());
                }
                HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                        .taskId(historicTaskInstance.getId())
                        .variableName("result").singleResult();
                if (ObjectUtil.isNotNull(historicVariableInstance)) {
                    if (Integer.parseInt(historicVariableInstance.getValue().toString()) == 1) {
                        myTask.setResult("同意");
                    }
                }
                if (ObjectUtil.isNotNull(historicTaskInstance.getAssignee())) {
                    myTask.setUserName(myCache.getUserName(historicTaskInstance.getAssignee()));
                }
                myTaskList.add(myTask);
            });
        }
        return myTaskList;
    }

    /**
     * 返回流程扩展的配置节点
     *
     * @return
     */
    public Result getProcessNodes(String processDefId) {
        Result result = new Result();
        ExtActNode extActNode = new ExtActNode();
        extActNode.setProcdefId(processDefId);
        List<ExtActNode> extActNodes = extActNodeMapper.select(extActNode);
        result.setData(extActNodes);
        return result;
    }

    /**
     * 更新配置节点
     *
     * @param extActNode
     * @return
     */
    public Result updateProcessNode(ExtActNode extActNode) {
        Result result = new Result();
        extActNodeMapper.updateByPrimaryKeySelective(extActNode);
        extActNode = extActNodeMapper.selectByPrimaryKey(extActNode);
        result.setData(extActNode);
        return result;
    }

    /**
     * 创建流程的扩展节点的配置
     * 1.如果是新部署流程直接创建对应的节点，没有配置信息
     * 2.如果是更新部署或者之前部署过一样的流程，则获取之前的配置信息，将之前配置信息的节点填到新创建的配置节点中去
     *
     * @param deploymentId
     * @return
     */
    private boolean createProcessExtActNode(String deploymentId) {
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if (ObjectUtil.isNull(processDefinition)) {
            return false;
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        if (ObjectUtil.isNull(bpmnModel)) {
            return false;
        }
        Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
        if (CollectionUtil.isEmpty(flowElements)) {
            return false;
        }
        List<ExtActNode> extActNodes = new ArrayList<>();

        List<ExtActNode> preNodes = getPreProcessExtActNode(processDefinition);
        flowElements.forEach(flowElement -> {
            if (flowElement instanceof UserTask) {
                ExtActNode extActNode = new ExtActNode();
                extActNode.setName(flowElement.getName());
                extActNode.setProcdefId(processDefinition.getId());
                extActNode.setJump(false);
                extActNode.setNodeId(flowElement.getId());
                extActNodes.add(extActNode);
            }
            if (flowElement instanceof SubProcess) {
                Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements();
                subFlowElements.forEach(subFlowElement -> {
                    if (subFlowElement instanceof UserTask) {
                        ExtActNode extActNode = new ExtActNode();
                        extActNode.setName(subFlowElement.getName());
                        extActNode.setProcdefId(processDefinition.getId());
                        extActNode.setJump(false);
                        extActNode.setNodeId(subFlowElement.getId());
                        extActNodes.add(extActNode);
                    }
                });
            }
        });

        if (CollectionUtil.isNotEmpty(preNodes)) {
            //同一流程第二次部署，有对应的节点，从以前的节点复制有用的信息到新建的node中
            Map<String, ExtActNode> preNodeMap = preNodes.stream()
                    .collect(Collectors.toMap(ExtActNode::getNodeId, node -> node));
            extActNodes.forEach(node -> {
                if (preNodeMap.containsKey(node.getNodeId())) {
                    ExtActNode preNode = preNodeMap.get(node.getNodeId());
                    node.setJump(preNode.getJump());
                    node.setName(preNode.getName());
                    node.setStatusColor(preNode.getStatusColor());
                    node.setStatus(preNode.getStatus());
                    node.setUrl(preNode.getUrl());
                }
            });
        }
        extActNodeMapper.insertList(extActNodes);
        return true;
    }


    /**
     * 获取同一个流程部署的上一版本的流程的扩展节点的定义
     *
     * @param processDefinition
     * @return
     */
    private List<ExtActNode> getPreProcessExtActNode(ProcessDefinition processDefinition) {
        List<ExtActNode> extActNodes = new ArrayList<>();
        List<ProcessDefinition> processDefinitions =
                repositoryService.createProcessDefinitionQuery()
                        .processDefinitionKey(processDefinition.getKey())
                        .orderByProcessDefinitionVersion().desc().list();
        if (processDefinitions.size() <= 1) {
            return extActNodes;
        }
        ProcessDefinition preProcessDef = processDefinitions.get(processDefinitions.size() - 2);
        ExtActNode extActNode = new ExtActNode();
        extActNode.setProcdefId(preProcessDef.getId());
        extActNodes = extActNodeMapper.select(extActNode);
        return extActNodes;
    }

    /**
     * 获取流程图片
     * @param response
     * @param processDefId
     * @throws IOException
     */
    public void getProcessPic(HttpServletResponse response, String processDefId) throws IOException {
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery().processDefinitionId(processDefId).singleResult();
        if (ObjectUtil.isNull(processDefinition)) {
            return;
        }

        // 获取BPMN模型对象
        BpmnModel model = repositoryService.getBpmnModel(processDefinition.getId());
        // 定义使用宋体
        String fontName = "宋体";
        // 获取流程实实例当前点的节点，需要高亮显示
//        List<String> currentActs = runtimeservice.getActiveActivityIds(processDefinition.getId());
        // BPMN模型对象、图片类型、显示的节点
        InputStream is = this.processEngine
                .getProcessEngineConfiguration()
                .getProcessDiagramGenerator()
                .generateDiagram(model, "png", new ArrayList<>(), new ArrayList<String>(),
                        fontName, fontName, fontName, null, 1.0);
        String filePath = fileUtil.getFilePath() + processDefinition.getDiagramResourceName();

        File file = new File(filePath);
        FileUtils.copyInputStreamToFile(is
                , file);
        fileUtil.writeFile(response, file);
    }


    public void getRunningProcessPicByInstantId(HttpServletResponse response, String processInstanceId) throws Exception {
        if (StringUtils.isEmpty(processInstanceId)) {
            throw new Exception("获取流程图片失败，流程实例不能为空!");
        }
        fileUtil.writeFile(response, actUtils.getDiagram(processInstanceId));
    }

    public void deleteProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = runtimeservice.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (ObjectUtil.isNull(processInstance)) {
            historyService.deleteHistoricProcessInstance(processInstanceId);
        } else {
            runtimeservice.deleteProcessInstance(processInstanceId, MyShiroUtils.getSessionUserName() + "删除流程");
        }
        List<HistoricTaskInstance> historicTaskInstanceList =
                historyService.createHistoricTaskInstanceQuery()
                        .processInstanceId(processInstanceId).list();
        if (CollectionUtil.isNotEmpty(historicTaskInstanceList)) {
            historicTaskInstanceList.forEach(historicTaskInstance -> {
                historyService.deleteHistoricTaskInstance(historicTaskInstance.getId());
            });
        }
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId).list();
        if (CollectionUtil.isNotEmpty(tasks)) {
            List<String> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
            taskService.deleteTasks(taskIds);
        }

    }
}
